/*
 * Author: Benedikt Sauter <sauter@ixbat.de>
 * All rights reserved.
 *
 * Short descripton of file:
 *
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above 
 *     copyright notice, this list of conditions and the following 
 *     disclaimer in the documentation and/or other materials provided 
 *     with the distribution.
 *   * Neither the name of the FH Augsburg nor the names of its 
 *     contributors may be used to endorse or promote products derived 
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "common.h"
#include "protocol.h"

#include <util/delay.h>


#include "93c46.h"

void flash_93c46_parser(char *buf)
{
   //UARTWrite("flash parser\r\n");
   switch(buf[3]) 
   {
      case CMD_93C46_INIT:
         flash_93c46_init_usb();
      break;
      case CMD_93C46_DEINIT:
         flash_93c46_deinit_usb();
      break;
      case CMD_93C46_READ:
         // read addr, length, buffer 
         flash_93c46_read_usb((unsigned char)buf[4], (int)buf[5], &buf[6]);
      break;
      case CMD_93C46_WRITE:
         flash_93c46_write_usb((unsigned char)buf[4], (int)buf[5], &buf[6]);
      break;
      default:
         //UARTWrite("default\r\n");
         answer[0] = buf[0];
         answer[1] = RSP_UNKOWN_CMD;
         answer[2] = 0;
         CommandAnswer(3);
   }
}

void flash_93c46_init_usb(void)
{
   answer[0] = CMD_93C46_INIT;
   answer[1] = (unsigned char) flash_93c46_init();
   answer[2] = 0;
   CommandAnswer(3);
}


int flash_93c46_init(void)
{
   //UARTWrite("init\r\n");
   SET_DDR_OUT(CS);
   SET_DDR_OUT(SK);

   SET_DDR_OUT(DI);
   SET_DDR_IN(DO);

   SET_DDR_OUT(DIN0);
   SET_DDR_OUT(DIN1);

   CLRPIN(DIN0);
   CLRPIN(DIN1);

   //SETPIN(DIN0) = D4 = low
   //SETPIN(DIN1) = D4 = low

   octopus.ports[16]=PIN_IN;
   octopus.ports[17]=PIN_OUT;
   octopus.ports[18]=PIN_OUT;
   octopus.ports[19]=PIN_OUT;

   return RSP_OK;
}

void flash_93c46_deinit_usb(void)
{
   answer[0]=CMD_93C46_DEINIT;
   answer[1]=(unsigned char)flash_93c46_deinit();
   answer[2]=0;
   CommandAnswer(3);
}

int flash_93c46_deinit(void)
{
   octopus.ports[16]=PIN_NONE;
   octopus.ports[17]=PIN_NONE;
   octopus.ports[18]=PIN_NONE;
   octopus.ports[19]=PIN_NONE;

   return RSP_OK;
}

void flash_93c46_read_usb(unsigned char address, unsigned int length, char * buf)
{
   answer[0]=CMD_93C46_READ;
   answer[1]=(unsigned char)flash_93c46_read(address,length,(char*)&answer[2]);
   answer[2+length] = 0;
   CommandAnswer(3+length);
}

int flash_93c46_read(unsigned char address, unsigned int length, char * buf)
{
   //UARTWrite("read\r\n");
   //SendHex((uint8_t)address);
   //SendHex(flash_93c46_read_word((uint8_t)address));
   unsigned int i;
   for(i = 0; i < length; i++)
      buf[i] = (char)flash_93c46_read_word((uint8_t)address+i);

   return RSP_OK;
}


void flash_93c46_write_usb(unsigned char address, unsigned int length, char * buf)
{
   answer[0]=CMD_93C46_WRITE;
   answer[1]=(unsigned char)flash_93c46_write(address,length, buf);
   answer[2]=0;
   CommandAnswer(3);
}

int flash_93c46_write(unsigned char address, unsigned int length, char * buf)
{
   unsigned int i;
   //UARTWrite("write\r\n");
   //SendHex((uint8_t)address); 
   //SendHex(length);
   //SendHex(buf[0]);

   for(i=0;i<length;i++)
      flash_93c46_write_word((uint8_t)address+i,buf[i]);

   return RSP_OK;
}



// Write enable must precede all programming modes.



void start_of_ins(void)
{
  CLRPIN(CS);
  CLRPIN(SK);
  CLRPIN(DI);
    
  _delay_ms(5);
  //SETPIN(DO);
  SETPIN(CS);
   //_delay_us(2);
  
  _delay_us(2);
  SETPIN(DI); //SB
  SETPIN(SK);
  _delay_us(2);
}


void send_sb_opcode(uint8_t ins)
{
  uint8_t    i;
  for( i =0; i <2; i ++)
  {
    CLRPIN(SK);
    if ( ins & 0x80 )  SETPIN(DI); else CLRPIN(DI);  
    _delay_us(2);
    SETPIN(SK);
    _delay_us(2);
    ins = ins <<1;  
  }
}

void send_address(uint8_t da)
{
  uint8_t i;
  da = da << 1;   
  for( i = 0; i <7; i ++)
  {
    CLRPIN(SK);
    if ( da & 0x80 )  SETPIN(DI); else CLRPIN(DI);  
    _delay_us(2);
    SETPIN(SK);
    _delay_us(2);
    da = da << 1;   
  }
}

void send_data_byte(uint8_t da)
{
  uint8_t i;
  for( i = 0; i <8; i ++)
  {
    CLRPIN(SK);
    if ( da & 0x80 )  SETPIN(DI); else CLRPIN(DI);  
    _delay_us(2);
    SETPIN(SK);
    _delay_us(2);
    da = da << 1;   
  }
}


uint8_t end_of_erase_write(void)
{
  
  uint8_t delay_tmp =0xff;
  CLRPIN(SK);
  CLRPIN(DI);
    _delay_us(2);
  SETPIN(SK);
    _delay_us(2);
  CLRPIN(SK);
  CLRPIN(CS);
    _delay_us(2);
  SETPIN(SK);
    _delay_us(2);
  CLRPIN(SK);
    _delay_us(2);
  SETPIN(CS);
  
  do{
    if(delay_tmp-- > 0) return 0;
    SETPIN(SK);
    _delay_us(2);
    CLRPIN(SK);
    _delay_us(2);
  } while (GETPIN(DO));
  
  return 1;
}

void end_of_erase_write_disable(void)
{
  CLRPIN(SK);
  CLRPIN(CS);
  CLRPIN(DI);
  SETPIN(DO);
}

void erase_write_enable(void)
{
  start_of_ins(); 
  send_sb_opcode(CMD_EWEN);  
  send_address(0x60);  
}

void erase_write_disable(void)
{
  start_of_ins();    
  send_sb_opcode(CMD_EWDS);
  send_address(0x00);
  end_of_erase_write_disable();    
}


uint8_t write_word_step(uint8_t add,uint8_t wd)
{
  start_of_ins();
  send_sb_opcode(CMD_WRITE);
  send_address(add);
  send_data_byte(wd);
  return 1;
  //return( end_of_erase_write() ); 
}

uint8_t erase_word(uint8_t add)
{
  start_of_ins();
  send_sb_opcode(CMD_ERASE);
  send_address(add);
  return( end_of_erase_write() ); 
}

uint8_t recv_data_word(void)
{
  uint8_t i;
  uint8_t da=0;
  CLRPIN(SK); 
  _delay_us(2);
  CLRPIN(DI); 
  
  for( i =0; i <8; i ++)
  {
    da *= 2; 
    SETPIN(SK);
    _delay_us(2);
    CLRPIN(SK);
    if (GETPIN(DO)) da++;
    _delay_us(2);
  }
  
  CLRPIN(CS); 
  SETPIN(SK);
    _delay_us(2);
  SETPIN(CS);
  CLRPIN(SK); 
    _delay_us(2);
  return da;
}

uint8_t flash_93c46_read_word(uint8_t add)
{
  //if(add < 0 && add > 127 )
  //  return 0;

  start_of_ins();  
  send_sb_opcode(CMD_READ);
  send_address(add);
  return( recv_data_word() );
}


uint8_t flash_93c46_write_word(uint8_t add,uint8_t wd)
{
  
//  if(add < 0 && add > 127 )
//    return 0;

  erase_write_enable();
  write_word_step(add,wd);
  //_delay_ms(1);

  return 1;
  do 
  {
    //write_word_step(add,wd);
    _delay_us(10);
  } while (flash_93c46_read_word(add)!=wd); 
  return 1;
}

